/*
 * Licensed Materials - Property of tenxcloud.com
 * (C) Copyright 2018 TenxCloud. All Rights Reserved.
 * 2018-06-13  @author lizhen
 */

package errors

import (
	"fmt"
	"net/http"

	jsoniter "github.com/json-iterator/go"
	"k8s.io/apimachinery/pkg/api/errors"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	k8serr "k8s.io/apimachinery/pkg/util/errors"
)

func NotFound(errOrMessage interface{}) (statusCode int, status *meta_v1.Status) {
	statusCode, status = httpResponse(
		errOrMessage,
		http.StatusNotFound,
		meta_v1.StatusReasonNotFound)
	return
}

func InternalServerError(errOrMessage interface{}) (statusCode int, status *meta_v1.Status) {
	statusCode, status = httpResponse(
		errOrMessage,
		http.StatusInternalServerError,
		meta_v1.StatusReasonInternalError)
	return
}

func BadRequest(errOrMessage interface{}) (statusCode int, status *meta_v1.Status) {
	statusCode, status = httpResponse(
		errOrMessage,
		http.StatusBadRequest,
		meta_v1.StatusReasonBadRequest)
	return
}

func Conflict(errOrMessage interface{}) (statusCode int, status *meta_v1.Status) {
	statusCode, status = httpResponse(
		errOrMessage,
		http.StatusConflict,
		meta_v1.StatusReasonConflict)
	return
}

func Unauthorized(errOrMessage interface{}) (statusCode int, status *meta_v1.Status) {
	statusCode, status = httpResponse(
		errOrMessage,
		http.StatusUnauthorized,
		meta_v1.StatusReasonUnauthorized)
	return
}

func Forbidden(errOrMessage interface{}) (statusCode int, status *meta_v1.Status) {
	statusCode, status = httpResponse(
		errOrMessage,
		http.StatusForbidden,
		meta_v1.StatusReasonForbidden)
	return
}

func adaptFromStatusError(err interface{}) (ok bool, statusCode int, status *meta_v1.Status) {
	var statusError *errors.StatusError
	if statusError, ok = err.(*errors.StatusError); ok {
		status = &statusError.ErrStatus
		statusCode = int(status.Code)
	}
	return
}

func statusError(errOrMessage interface{}) (status *meta_v1.Status) {
	var message string
	err, ok := errOrMessage.(error)
	if ok {
		message = err.Error()
	} else if message, ok = errOrMessage.(string); !ok {
		message = fmt.Sprint(errOrMessage)
	}
	return &meta_v1.Status{
		Message: message,
		Status:  meta_v1.StatusFailure,
	}
}

func httpResponse(errOrMessage interface{}, code int, reason meta_v1.StatusReason) (statusCode int, status *meta_v1.Status) {
	var ok bool
	if ok, statusCode, status = adaptFromStatusError(errOrMessage); ok {
		return statusCode, status
	}
	status = statusError(errOrMessage)
	status.Reason = reason
	status.Code = int32(code)
	statusCode = code
	return
}

var (
	json = jsoniter.ConfigFastest
)

type errs struct {
	errs []error
}

func (e errs) Error() string {
	if len(e.errs) == 1 {
		return e.errs[0].Error()
	}
	return k8serr.NewAggregate(e.errs).Error()
}

func (e *errs) append(err error) *errs {
	e.errs = append(e.errs, err)
	return e
}

func (e errs) MarshalJSON() ([]byte, error) {
	return json.Marshal(e.errs)
}

func (e *errs) UnmarshalJSON(data []byte) error {
	return json.Unmarshal(data, &(e.errs))
}

func Append(mayByErrs error, ers ...error) (err error) {
	var es *errs
	if e, ok := mayByErrs.(*errs); ok {
		es = e
	} else {
		es = &errs{errs: []error{mayByErrs}}
	}
	if ers != nil && len(ers) > 0 {
		for _, er := range ers {
			es = es.append(er)
		}
	}
	err = es
	return
}
